import Overlay from "ol/Overlay";
import { listen, unlistenByKey } from "ol/events";


import "./Tooltip.css";

const MIN_Z_INDEX = 0;
const MAX_Z_INDEX = 9999;

const PLACEMENTS = {
  TOP: "top",
  BOTTOM: "bottom",
  LEFT: "left",
  RIGHT: "right",
};

const POSITIONINGS = {
  TOP: "bottom-center",
  BOTTOM: "top-center",
  LEFT: "center-right",
  RIGHT: "center-left",
};

const PLACEMENT_MAP_POSITIONING = [
  [PLACEMENTS.TOP, POSITIONINGS.TOP],
  [PLACEMENTS.RIGHT, POSITIONINGS.RIGHT],
  [PLACEMENTS.BOTTOM, POSITIONINGS.BOTTOM],
  [PLACEMENTS.LEFT, POSITIONINGS.LEFT],
];

/**
 * 根据positioning获取placement
 * @param {string} positioning - positioning值
 * @returns {string} placement值
 */
const toPlacement = positioning => {
  switch (positioning) {
    case POSITIONINGS.TOP:
      return PLACEMENTS.TOP;
    case POSITIONINGS.RIGHT:
      return PLACEMENTS.RIGHT;
    case POSITIONINGS.BOTTOM:
      return PLACEMENTS.BOTTOM;
    case POSITIONINGS.LEFT:
      return PLACEMENTS.LEFT;
    default:
      return "";
  }
};

/**
 * 根据placement获取positioning
 * @param {string} placement - placement值
 * @returns {string} positioning值
 */
const toPositioning = placement => {
  switch (placement) {
    case PLACEMENTS.TOP:
      return POSITIONINGS.TOP;
    case PLACEMENTS.RIGHT:
      return POSITIONINGS.RIGHT;
    case PLACEMENTS.BOTTOM:
      return POSITIONINGS.BOTTOM;
    case PLACEMENTS.LEFT:
      return POSITIONINGS.LEFT;
    default:
      return "";
  }
};

/**
 * @class Tooltip
 * @extends {Overlay}
 * @classdesc 自定义的 OpenLayers 提示框覆盖物类，继承自 ol/Overlay
 *
 * @param {Object} options - 配置选项
 * @param {string} [options.groupId='default'] - 提示框分组ID
 * @param {boolean} [options.visibility=true] - 是否默认可见
 * @param {string} [options.placement='top'] - 提示框位置，可选值：'top' | 'bottom' | 'left' | 'right'
 * @param {HTMLElement|string} options.element - 提示框内容元素或HTML字符串
 * @param {number} [options.maxZoom=Infinity] - 最大显示缩放级别
 * @param {number} [options.minZoom=0] - 最小显示缩放级别
 * @param {boolean} [options.isUseContainer=true] - 是否使用自定义的提示框容器
 * @param {number} [options.zIndex=0] - 提示框的z-index
 *
 * @example
 * // 创建一个顶部显示的提示框
 * const tooltip = new Tooltip({
 *   element: '<div>提示内容</div>',
 *   placement: 'top'
 * });
 *
 * @property {boolean} isUseContainer - 是否使用自定义的Tooltip容器
 * @property {number} minZoom - 最小缩放级别
 * @property {number} maxZoom - 最大缩放级别
 * @property {Array<number>} listenKeys - 事件监听器数组
 * @property {boolean} isOnZoomRange - 是否在缩放范围内
 */
class Tooltip extends Overlay {
  constructor(options) {
    const { element, ...rest } = options;
    super(rest);

    /**
     * 提示框分组ID
     * @type {string}
     * @protected
     */
    this.groupId = options.groupId !== undefined ? options.groupId : "default";

    /**
     * 是否使用自定义的Tooltip容器
     * @type {boolean}
     * @protected
     */
    this.isUseContainer =
      options.isUseContainer !== undefined ? options.isUseContainer : true;

    /**
     * 最小缩放级别
     * @type {number}
     * @protected
     */
    this.minZoom = options.minZoom !== undefined ? options.minZoom : 0;

    /**
     * 最大缩放级别
     * @type {number}
     * @protected
     */
    this.maxZoom = options.maxZoom !== undefined ? options.maxZoom : Infinity;

    /**
     * 事件监听器
     * @type {Array}
     * @private
     */
    this.listenKeys = [];

    /**
     * 是否在缩放范围内
     * @type {boolean}
     * @private
     */
    this.isOnZoomRange = true;

    // 注册 `change:visibility` 事件侦听器
    this.on("change:visibility", this.handleTooltipVisibilityChanged);
    this.on("change:placement", this.handleTooltipPlacementChanged);
    this.on("change:positioning", this.handleTooltipPositioningChanged);
    this.on("change:map", this.handleTooltipMapChanged);
    this.on("change:zIndex", this.handleTooltipZIndexChanged);

    // 设置元素
    this.setElement(options.element);

    // 设置可见性
    this.set(
      "visibility",
      options.visibility !== undefined ? options.visibility : true
    );

    // 设置位置方位
    this.set(
      "placement",
      options.placement !== undefined ? options.placement : "top"
    );
    // 设置z-index
    this.setZIndex(options.zIndex !== undefined ? options.zIndex : MIN_Z_INDEX);
  }

  /**
   * @protected
   */
  handleTooltipVisibilityChanged() {
    const element = this.getElement();
    if (element) {
      element.style.display = this.getVisibility() ? "flex" : "none";
    } else {
      this.once("change:element", this.handleTooltipVisibilityChanged);
    }
  }

  /**
   * @protected
   *
   */
  handleTooltipPlacementChanged() {
    const placement = this.getPlacement();
    const positioning = toPositioning(placement);
    if (positioning) {
      this.setPositioning(positioning);
    }

    if (this.isUseContainer) {
      this.getElement().className = `ol-custom-tooltip ${placement}`;
      // 设置偏移
      switch (placement) {
        case PLACEMENTS.TOP:
          this.setOffset([0, -10]);
          break;
        case PLACEMENTS.BOTTOM:
          this.setOffset([0, 10]);
          break;
        case PLACEMENTS.LEFT:
          this.setOffset([-10, 0]);
          break;
        case PLACEMENTS.RIGHT:
          this.setOffset([10, 0]);
          break;
        default:
          this.setOffset([0, 0]);
      }
    }
  }

  /**
   * @protected
   */
  handleTooltipPositioningChanged() {
    const positioning = this.getPositioning();
    const placement = toPlacement(positioning);
    if (placement) {
      this.setPlacement(placement);
    }
  }

  /**
   * @protected
   */
  handleTooltipMapChanged() {
    const map = this.getMap();

    if (map) {
      // 监听地图缩放事件(实现对Tooltip的缩放范围控制)
      if (this.minZoom > 0 && this.maxZoom < Infinity) {
        this.handleMapZoomChange();

        this.listenKeys.push(
          listen(map, "moveend", this.handleMapZoomChange, this)
        );
      }
    } else {
      // 移除所有事件监听
      this.removeAllListeners();
    }
  }

  /**
   * @protected
   */
  handleMapZoomChange() {
    const zoom = this.getMap().getView().getZoom();
    const visibility = zoom >= this.minZoom && zoom <= this.maxZoom;

    console.log(
      "zoom change",
      zoom,
      visibility ? "在显示范围内" : "已超出显示范围"
    );

    this.isOnZoomRange = true;
    this.setVisibility(visibility);
    this.isOnZoomRange = visibility;
  }

  /**
   * @protected
   */
  handleTooltipZIndexChanged() {
    const element = this.getElement();
    const zIndex = this.getZIndex();
    if (element) {
      const parentElement = element.parentElement;
      parentElement.style.zIndex = zIndex;

      parentElement.addEventListener("mouseenter", function () {
        this.style.zIndex = MAX_Z_INDEX;
      });

      parentElement.addEventListener("mouseleave", function () {
        this.style.zIndex = zIndex;
      });
    } else {
      this.once("change:element", this.handleTooltipZIndexChanged);
    }
  }

  /**
   *
   * @override
   */
  setElement(element) {
    let _element = this.formatElement(element);

    super.setElement(_element);
  }

  /**
   * 格式化元素
   * @param {HTMLElement | string} element - 元素
   * @returns {HTMLElement} 格式化后的元素
   *
   */
  formatElement(element) {
    let _element = element;

    // DOMString 转换为 HTMLElement
    if (typeof element === "string") {
      const div = document.createElement("div");
      div.innerHTML = element;
      _element = div.firstElementChild;
    }

    // 使用自定义的模版
    if (this.isUseContainer) {
      const container = document.createElement("div");
      container.className = `ol-custom-tooltip ${this.getPlacement()}`;
      container.appendChild(_element);
      _element = container;
    }

    return _element;
  }

  /**
   * 获取Tooltip的组ID
   * @returns {string} 当前组ID
   * @description 返回Tooltip所属的组ID
   */
  getGroupId() {
    return this.groupId;
  }

  /**
   * 获取Tooltip的最大缩放级别
   * @returns {number} 当前最大缩放级别
   * @description 返回Tooltip的最大缩放级别
   */
  getMaxZoom() {
    return this.maxZoom;
  }

  /**
   * 获取Tooltip的最小缩放级别
   * @returns {number} 当前最小缩放级别
   * @description 返回Tooltip的最小缩放级别
   */
  getMinZoom() {
    return this.minZoom;
  }

  /**
   * 设置Tooltip的可见性
   * @param {boolean} visibility - 是否可见
   * @description 设置Tooltip的可见性
   */
  setVisibility(visibility) {
    if (!this.isOnZoomRange) return;
    this.set("visibility", visibility);
  }

  /**
   * 获取Tooltip的可见性
   * @returns {boolean} 当前可见性状态
   * @description 返回Tooltip的当前可见性状态
   */
  getVisibility() {
    return this.get("visibility");
  }

  /**
   * 设置Tooltip的位置方位
   * @param {string} placement - 位置方位，可选值：'top' | 'bottom' | 'left' | 'right'
   * @description 设置Tooltip的位置方位
   */
  setPlacement(placement = "top") {
    this.set("placement", placement);
  }

  /**
   * 获取Tooltip的位置方位
   * @returns {string} 当前位置方位
   * @description 返回Tooltip的当前位置方位
   */
  getPlacement() {
    return this.get("placement");
  }

  /**
   * 设置Tooltip的z-index
   * @param {number} zIndex - z-index值
   * @description 设置Tooltip的z-index，同时更新DOM元素的z-index
   */
  setZIndex(zIndex) {
    let _zIndex = zIndex;

    if (zIndex < MIN_Z_INDEX) {
      _zIndex = MIN_Z_INDEX;
    } else if (zIndex > MAX_Z_INDEX) {
      _zIndex = MAX_Z_INDEX;
    }

    this.set("zIndex", _zIndex);
  }

  /**
   * 获取Tooltip的z-index
   * @returns {number} 当前z-index值
   * @description 返回Tooltip的当前z-index值
   */
  getZIndex() {
    return this.get("zIndex");
  }

  /**
   * 移除所有事件监听
   * @description 移除所有事件监听
   */
  removeAllListeners() {
    this.listenKeys.forEach(key => {
      unlistenByKey(key);
    });
    this.listenKeys = [];
  }

  /**
   * 销毁Tooltip实例
   * @description 清理所有资源，包括事件监听器、DOM元素和属性
   */
  dispose() {
    const map = this.getMap();
    map && map.removeOverlay(this);

    // 移除所有事件监听
    this.removeAllListeners();

    // 移除Tooltip自身的事件监听

    // 清理DOM元素
    const element = this.getElement();
    if (element && element.parentNode) {
      element.parentNode.removeChild(element);
    }

    // 清理属性
    this.set("groupId", null);
    this.set("placement", null);
    this.set("visibility", null);

    // 检查父类是否有dispose方法
    if (typeof super.dispose === "function") {
      super.dispose();
    }
  }
}

export {
  PLACEMENTS,
  POSITIONINGS,
  PLACEMENT_MAP_POSITIONING,
  toPlacement,
  toPositioning,
};

export default Tooltip;
